#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>

#include "kcp_client.h"
#include "utils.h"
#include "custom.h"
#include "msg.h"

extern int device_type;

typedef struct {
	char *srv_ip_;
	unsigned short srv_port_;
	ikcpcb *pkcp_;
	int sockfd_;
	struct sockaddr_in peer_addr_;
}kcpEntry;

static kcpEntry *g_kcp_entry = NULL;

#define KEEPALIVE_INTERVAL (5)

static int UdpSend(const char *buf, int len, ikcpcb *kcp, void *user)
{
    kcpEntry *kcp_entry = (kcpEntry *)user;

    int n = sendto(kcp_entry->sockfd_, buf, len, 0, (struct sockaddr *)&kcp_entry->peer_addr_, sizeof(struct sockaddr_in));
    if (n >= 0) 
	{   
		if (n >= 48)
		{
			IUINT32 v = (IUINT32)buf[24]&0xff | (IUINT32)((buf[25]&0xff)<<8) | (IUINT32)((buf[26]&0xff)<<16) | (IUINT32)((buf[27]&0xff)<<24);
			if (v == kcp->conv)
			{
				//printf("+++ UdpSend, kcp_entry %d bytes, buf: [%s]\n", n ,buf+24+24);//24+24字节的KCP头部

			}
			else
			{
				//printf("#### UdpSend, kcp_entry %d bytes, buf: [%s]\n", n ,buf+24);//24+24字节的KCP头部
			}
		}
		//else if (n > 24)
		//	printf("---UdpSend, kcp_entry %d bytes, buf: [%s]\n", n, buf+24);//24字节的KCP头部
			
        return n;
    } 
	else 
	{
        printf("Waring: UdpSend failed\n");
        return -1;
    }
}

void KCPInit(char *srv_ip, unsigned short srv_port)
{	
	g_kcp_entry = (kcpEntry *)malloc(sizeof(kcpEntry));

	g_kcp_entry->srv_ip_ = strdup(srv_ip);
	g_kcp_entry->srv_port_ = srv_port;
	g_kcp_entry->sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
	
	if(g_kcp_entry->sockfd_ < 0)
	{
		perror("socket error！");
		exit(1);
	}

	int flags = fcntl(g_kcp_entry->sockfd_, F_GETFL, 0);
	flags |= O_NONBLOCK;

	if(fcntl(g_kcp_entry->sockfd_, F_SETFL, flags) == -1)
	{
		printf("Can not set ASY module, error\n");
	}

	bzero(&g_kcp_entry->peer_addr_, sizeof(g_kcp_entry->peer_addr_));
	
	g_kcp_entry->peer_addr_.sin_family=AF_INET;
    g_kcp_entry->peer_addr_.sin_addr.s_addr = inet_addr((char*)g_kcp_entry->srv_ip_);
    g_kcp_entry->peer_addr_.sin_port = htons(g_kcp_entry->srv_port_);
	
	printf("sockfd = %d ip = %s  port = %d\n", 
		g_kcp_entry->sockfd_, g_kcp_entry->srv_ip_, g_kcp_entry->srv_port_);

	struct timeval tv;
	gettimeofday(&tv, NULL);
	
	IUINT32 conv = (IUINT32)tv.tv_sec - (IUINT32)tv.tv_usec;
	
	ikcpcb *kcp = ikcp_create(conv, (void *)g_kcp_entry);
	kcp->output = UdpSend;
	ikcp_nodelay(kcp,0, 10, 0, 0);//(kcp1, 0, 10, 0, 0); 1, 10, 2, 1
	ikcp_wndsize(kcp, 128, 128);
	
	g_kcp_entry->pkcp_ = kcp;
}

void KCPLoop()
{
	int seq = 0;
	int number = 0;
	int maxfd, nready;
	fd_set read_set;
	struct timeval tv;
	int n,ret;
	int index = 1;
	int cnt = 0;
	struct sockaddr_in from;
	uint8_t report = 0;
	uint8_t real_get;
	time_t start = 0;
	uint8_t query = 0;

	unsigned int len = sizeof(struct sockaddr_in);
	char *resp_msg = GetRespMsg();
	char *period_msg = GetPeriodMsg();
	int snd_pipe = GetSndPipeFd();

	maxfd = g_kcp_entry->sockfd_>snd_pipe?g_kcp_entry->sockfd_:snd_pipe;
	maxfd += 1;
	
	FD_ZERO(&read_set);

	while(1)
	{
		FD_ZERO(&read_set);
		FD_SET(g_kcp_entry->sockfd_, &read_set);
		FD_SET(snd_pipe, &read_set);

		tv.tv_sec = 0;
		tv.tv_usec = 10*1000;

		real_get = 0;

		nready = select(maxfd, &read_set, NULL, NULL, &tv);

		time_t current = time(NULL);
		int diff = current - start;
		
		report = 0;

		if (diff >= KEEPALIVE_INTERVAL)
		{
			report = 1;
			start = current;
		}

		if (nready < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			else
			{
				perror("select error");
				exit(-1);
			}
		}

		if (nready > 0)
		{
			//recv from remote server 
			if (FD_ISSET(g_kcp_entry->sockfd_, &read_set))
			{
				char buf[1500] = {0};
				n = recvfrom(g_kcp_entry->sockfd_, buf, 1500, 0, (struct sockaddr *)&from, &len);	
				
				//maybe kcp control msg, we don't care it。 
				ret = ikcp_input(g_kcp_entry->pkcp_, buf, n);	
				if(ret < 0)
				{
					continue;			
				}

				char buf_[1500] = {0};
				
				while(1)
				{	
					//get real payload 	
					ret = ikcp_recv(g_kcp_entry->pkcp_, buf, n);
					if (ret > 0)
					{
						real_get = 1;
					}
					else if(ret < 0)
					{
						break;
					}
				}

				if (1 == real_get)
				{
					if (strcmp(buf, "OK"))
					{
						ikcp_send(g_kcp_entry->pkcp_, resp_msg, strlen(resp_msg) + 1);
						//push back to queue
						MsgNode *comm_msg = (MsgNode *)malloc(sizeof(MsgNode));
						comm_msg->cmd_.seq_ = seq++;
						comm_msg->cmd_.msg_ = strdup(buf);
						
						PushBack2RcvQueue(comm_msg);
					}

					//printf("[%s:%d], data: %s\n",
					//		inet_ntoa(g_kcp_entry->peer_addr_.sin_addr), 
					//		ntohs(g_kcp_entry->peer_addr_.sin_port), buf); 
				}
			}

			//read from local, then send to remote
			if (FD_ISSET(snd_pipe, &read_set))
			{
				Command cmd;
				if ((n = read(snd_pipe, (void *)&cmd, sizeof(cmd))) > 0)
				{
					printf("read from pipe, bytes is %d, seq: %d, msg: %s\n", n, cmd.seq_, cmd.msg_);
					
					ikcp_send(g_kcp_entry->pkcp_, cmd.msg_, strlen(cmd.msg_) + 1);

					free(cmd.msg_);
				}	
			}
		}

		//insert keepalive msg to sending queue
		if (1 == report) 
		{		
			ikcp_send(g_kcp_entry->pkcp_, period_msg, strlen(period_msg) + 1);
		}

		if (1 == real_get)
		{
			if ((1 == device_type) && (0 == query))
			{
				QueryDevList();
				query = 1;
			}
		}

		//need loop
		ikcp_update(g_kcp_entry->pkcp_, iclock());
	}
}

